﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OverStore.Exceptions;
using OverStore.Scoping;
using OverStore.Runtime.ObjectRepository;
using OverStore.Runtime.CollectionPersisting;
using OverStore.Scoping.Manager;

namespace OverStore.Runtime.PersistenceServiceProvider
{
    /// <summary>
    /// Provide access to different kinds of persistence services: <see cref="IEntityRegistry{TInstance, TIdentifier}"/>, 
    /// <see cref="IRepository{TInstance}"/> using type of instance persisted.
    /// </summary>
    /// <remarks>
    /// Current implementation of uses exact type of instance for match corresponding type of service and does not 
    /// perform search of compatible service.
    /// </remarks>
    public sealed class DefaultPersistenceServiceProvider : IPersistenceServiceProvider, IDisposable
    {
        private readonly OperationScopeManager _OperationScopeManager;
        private readonly Dictionary<Type, List<object>> _CollectionPersisters = new Dictionary<Type, List<object>>();
        private bool _Disposed;
        private Dictionary<Type, IRepositoryBase> _ObjectRepositoryTypeIndex;

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultPersistenceServiceProvider"/> class.
        /// </summary>
        /// <param name="operationScopeManager">The operation scope manager.</param>
        public DefaultPersistenceServiceProvider(OperationScopeManager operationScopeManager)
        {
            if (Object.ReferenceEquals(operationScopeManager, null))
                throw new ArgumentNullException("operationScopeManager");
            _OperationScopeManager = operationScopeManager;
        }

        /// <summary>
        /// Initializes the persistence service provider with repository and object reader.
        /// </summary>
        public void Initialize(IEnumerable<IRepositoryBase> repositories)
        {
            if (repositories == null)
                throw new ArgumentNullException("repositories");
            _ObjectRepositoryTypeIndex = repositories.ToDictionary(r => r.Type);
        }

        /// <summary>
        /// Find <see cref="IRepository{TInstance}"/> implementation for specified type of Instance.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance to get repository for it.</typeparam>
        /// <returns>
        ///     <see cref="IRepository{TInstance}"/> for specified <typeparamref name="TInstance">type</typeparamref>.
        /// </returns>
        /// <exception cref="TypePersistenceNotSupportedException">No object repository provided for specified type.</exception>
        [DebuggerStepThrough]
        public IRepository<TInstance> GetRepository<TInstance>() where TInstance : class
        {
            IRepositoryBase result;
            if (_ObjectRepositoryTypeIndex.TryGetValue(typeof(TInstance), out result))
                return (IRepository<TInstance>)result;
            throw new TypePersistenceNotSupportedException(typeof(TInstance));
        }

        /// <summary>
        /// Gets the collection persister for the specified collection.
        /// </summary>
        public ICollectionPersister<TInstance> GetCollectionPersister<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            List<object> collectionPersisters;
            if (!_CollectionPersisters.TryGetValue(typeof(TInstance), out collectionPersisters))
            {
                collectionPersisters = new List<object>();
                collectionPersisters.Add(new GenericCollectionPersister<TInstance>(_OperationScopeManager, this));
                _CollectionPersisters[typeof(TInstance)] = collectionPersisters;
            }
            foreach (var r in collectionPersisters.OfType<ICollectionPersister<TInstance>>())
                if (r.SupportCollectionPersisting(collection))
                    return r;
            throw new PersistenceOperationIsNotSupportedException();
        }

        /// <summary>
        /// Iterate throught all registered <see cref="IRepositoryBase"/> repositories.
        /// </summary>
        /// <returns>
        /// The set of <see cref="IRepositoryBase"/> objects.
        /// </returns>
        public IEnumerable<IRepositoryBase> AllRepositories()
        {
            return _ObjectRepositoryTypeIndex.Values;
        }

        /// <summary>
        /// Return registry for specific instance type and specified identifier type.
        /// The method should returns same <see cref="IEntityRegistry{TInstance, TIdentifier}"/> instance for same
        /// type arguments for allow registry caching.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the instance identifier.</typeparam>
        /// <returns>
        ///     <see cref="IEntityRegistry{TInstance, TIdentifier}"/> for specified type and identifier.
        /// </returns>
        /// <exception cref="TypePersistenceNotSupportedException">No registry provided for specified instance type.</exception>
        /// <exception cref="IdentifierTypeIsInvalidException">Registry provided for specified instance type but has
        /// different identifier type than specified.</exception>
        public IEntityRegistry<TInstance, TIdentifier> GetRegistry<TInstance, TIdentifier>() where TInstance : class
        {
            try
            {
                return (IEntityRegistry<TInstance, TIdentifier>)_ObjectRepositoryTypeIndex[typeof(TInstance)];
            }
            catch (InvalidCastException)
            {
                throw new IdentifierTypeIsInvalidException(typeof(TInstance), typeof(TIdentifier));
            }
            catch (KeyNotFoundException)
            {
                throw new TypePersistenceNotSupportedException(typeof(TInstance));
            }
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        public void Dispose()
        {
            if (!_Disposed)
            {
                foreach (var item in _ObjectRepositoryTypeIndex.Values)
                {
                    var disposable = item as IDisposable;
                    if (disposable != null)
                        disposable.Dispose();
                }
                _Disposed = true;
            }
        }

        #region Repository Members

        /// <summary>
        ///     <para> Updates data of specified instance using data extracted from data storage, reload instance aggregates and
        /// mark instance as unchanged, so further saving will not cause data storage updating (except if object was changed).
        /// </para>
        ///     <para> If specified <paramref name="instance"/> is not registered in repository, method also registers it.
        /// If deffer loading is supported and enabled, the object will only be registered for
        /// loading, and loading itself will be performed by request.</para>
        /// </summary>
        /// <typeparam name="TInstance"></typeparam>
        /// <param name="instance">The instance to reload.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same  already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        public bool Reload<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            return repository.Reload(instance);
        }

        /// <summary>
        /// Clears collection and fills it up with object read from parameterless reader for instance.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Collection contains instance of object with same key already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void ReloadCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            persister.Reload(collection);
        }

        /// <summary>
        /// Clears collection and fills it up with object read from parameterless reader for instance.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <param name="parameter">The parameter used for get data for loading.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Collection contains instance of object with same key already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameters of specified type not supported.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void ReloadCollection<TInstance, TParameter>(IEnumerable<TInstance> collection, TParameter parameter)
            where TInstance : class
            where TParameter : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            persister.Reload<TParameter>(collection, parameter);
        }

        /// <summary>
        ///     <para>
        /// Saves changes made in <paramref name="instance"/> in data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage.
        /// </para>
        ///     <para>
        /// If instance supports deffer loading and is not loaded yet, method does not perform any
        /// actions, because changes can not be made without instance loading.
        /// </para>
        ///     <para>
        /// Method does not accept instance changes, but types implements current method should consider possibility
        /// of calling this method few times with same instance, in case of cycle references, for example.
        /// Repeated calls should not cause repeated saving, so method should take steps to avoid repeated saving.
        /// For example, instance may internally maintain list of saved instances, checks this list on saving and
        /// clear this list in see cref="IRepository{TInstance}.AcceptChanges" method.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to save changes.</param>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void Save<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            repository.Save(instance);
        }

        /// <summary>
        /// 	<para>
        /// Saves changes made in each item of collection to data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage.
        /// Also method saves instances were removed from collection, if collection type persister supports this feature.
        /// </para>
        /// 	<para>
        /// If collection supports deffer loading and is not loaded yet, method does not perform any
        /// actions, because changes can not be made without collection loading.
        /// </para>
        /// 	<para>
        /// Method does not accept collection changes, but types implements current method should consider possibility
        /// of calling this method few times with same instance, in case of cycle references, for example.
        /// Repeated calls should not cause repeated saving, so method should take steps to avoid repeated saving.
        /// For example, instance may internally maintain list of saved instances, checks this list on saving and
        /// clear this list in see cref="IRepository{TInstance}.AcceptChanges" method.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection"></param>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void SaveCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            persister.Save(collection);
        }

        /// <summary>
        /// Mark <paramref name="instance"/> as deleted.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to mark as deleted.</param>
        /// <remarks>
        /// Method itself does not save any changes to storage. For actually delete <paramref name="instance"/>
        /// from storage, call <see cref="IRepository{TInstance}.Save"/> method.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as deleted.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void MarkAsDeleted<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            repository.MarkAsDeleted(instance);
        }

        /// <summary>
        /// Marks every item in collection as deleted. Method does not clears collection content.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <remarks>
        /// Method itself does not save any changes to storage. For actually delete <paramref name="instance"/>
        /// from storage, call <see cref="IRepository{TInstance}.Save"/> method.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as deleted.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void MarkCollectionAsDeleted<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            persister.MarkAsDeleted(collection);
        }

        /// <summary>
        /// Marks <paramref name="instance"/> as needed to be physically saved to storage even 
        /// if <paramref name="instance"/> data are not changed.
        /// This method may be called any times untill AcceptChanges method called, but instance will be saved only once.
        /// </summary>
        /// <remarks>
        /// Use this method in couple with optimistic locking (version checking) to organize object hierarchy into 'atom' group. 
        /// Call this method on successful instance saving using other members of 'atom' object group, then save these objects.
        /// </remarks>
        /// <typeparam name="TInstance">Type of instance.</typeparam>
        /// <param name="instance">Instance to mark as dirty.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with the same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as dirty.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void MarkAsDirty<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            repository.MarkAsDirty(instance);
        }

        /// <summary>
        /// Marks every item in collection as needed to be physically saved to storage even if item data are not changed.
        /// This method may be called any times untill AcceptChanges method called, but instance will be saved only once.
        /// </summary>
        /// <typeparam name="TInstance">Type of instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <remarks>
        /// Use this method in couple with optimistic locking (version checking) to organize objects hierarchy into 'atom' group.
        /// Call this method on successful instance saving using other members of 'atom' object group, then save these objects.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with the same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as dirty.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void MarkCollectionAsDirty<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            foreach (var item in collection)
                MarkAsDirty(item);
        }

        /// <summary>
        ///     <para> Registers <paramref name="instance"/> in repository, if it is not registered yet. After registration
        /// <paramref name="instance"/> become available by its identifier. </para>
        ///     <para> If same instance already registered in repository, method does not perform any action.</para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to registers in repository.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured during add object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void Add<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            repository.Add(instance);
        }

        /// <summary>
        /// 	<para> Registers every item of collection in session , if it is not registered yet.
        /// After registration item become available by its identifier. </para>
        /// 	<para> If same instance already registered in repository, method does not perform any action.</para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured during add object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void AddCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            persister.Add(collection);
        }

        /// <summary>
        /// Joins specified instance or collection to repository if required, applies changes made to <paramref name="instance"/> and attach aggregates of specified instance.
        /// If specified instance is collection, attach each items and deletes items deleted from collection since detaching.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to attach.</param>
        /// <returns>
        /// The instance registered in repository contains data of <paramref name="instance"/>.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during Attach object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public TInstance Attach<TInstance>(TInstance instance)
            where TInstance : class
        {
            var repository = GetRepository<TInstance>();
            return repository.Attach(instance);
        }

        /// <summary>
        /// Joins every item of collection to repository if required, applies changes made to <paramref name="instance"/> and attach aggregates of specified instance.
        /// If specified instance is collection, attach each items and deletes items deleted from collection since detaching.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection"></param>
        /// <returns>
        /// The instance registered in repository contains data of <paramref name="instance"/>.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during Attach object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public IEnumerable<TInstance> AttachCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class
        {
            var persister = GetCollectionPersister<TInstance>(collection);
            return persister.Attach(collection);
        }

        #endregion

        #region Registry members

        /// <summary>
        /// Get identifier for object registered in session.
        /// If object is not registered in session, it will be placed in session.
        /// If object does not have identifier yet, the identifier will be assigned to it
        /// corresponding to identification rules.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="instance">The instance to get identifier.</param>
        /// <returns>The instance identifier.</returns>
        /// <exception cref="PersistingRuntimeException">Error occured during get identifier.</exception>
        public TIdentifier GetIdentifier<TInstance, TIdentifier>(TInstance instance)
            where TInstance : class
        {
            var registry = GetRegistry<TInstance, TIdentifier>();
            return registry.GetIdentifier(instance);
        }

        /// <summary>
        /// Remember new identifier for instance registered in session.
        /// Call AcceptChanges method to actually apply new identifier to instance and
        /// register new identifier in session.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="instance">The instance to update its identifier.</param>
        /// <param name="newIdentifier">The new identifier.</param>
        /// <exception cref="PersistingRuntimeException">Error occured during update identifier.</exception>
        /// <remarks>
        /// Identifier updating is required if identifiers are generated by database.
        /// Delayed identifier updating is required because if error occured due updating the instance
        /// or other instance in batch, identifier value generated by database becomes invalid.
        /// </remarks>
        public void PutNewIdentifier<TInstance, TIdentifier>(TInstance instance, TIdentifier newIdentifier)
            where TInstance : class
        {
            var registry = GetRegistry<TInstance, TIdentifier>();
            registry.PutNewIdentifier(instance, newIdentifier);
        }

        /// <summary>
        /// Get object registered in session.
        /// If object is not found, the object will be constructed, registered in session, reloaded and
        /// returned.
        /// If object does not registered in session, it will be constructed
        /// using data extracted from default gateway.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="identifier">The identifier of instance to get.</param>
        /// <returns>The instance with specified identifier.</returns>
        /// <exception cref="PersistingRuntimeException">Error occured during get object by identifier.</exception>
        public TInstance GetInstance<TInstance, TIdentifier>(TIdentifier identifier)
            where TInstance : class
        {
            var registry = GetRegistry<TInstance, TIdentifier>();
            return registry.GetInstance(identifier);
        }

        /// <summary>
        /// Gets entity with specified identifier. If entity registered in session it will be returned.
        /// Otherwise it will loaded from data storage. If there is no entity with specified identifier in data storage,
        /// method returns default value of <typeparamref name="TInstance"/> type.
        /// </summary>
        /// <typeparam name="TInstance"></typeparam>
        /// <typeparam name="TIdentifier"></typeparam>
        /// <param name="identifier">The identifier of entity to get from registry.</param>
        /// <returns>
        /// The entity with specified identifier or default value of <typeparamref name="TEntity"/> type of there is no
        /// entity with specified identifier exists in data storage.
        /// </returns>
        public TInstance GetInstanceOrDefault<TInstance, TIdentifier>(TIdentifier identifier)
            where TInstance : class
        {
            var registry = GetRegistry<TInstance, TIdentifier>();
            return registry.GetInstanceOrDefault(identifier);
        }

        /// <summary>
        /// Get object registered in session.
        /// If object is not found, the object will be constructed, registered in session and reloaded
        /// using data passed as <paramref name="data"/> argument.
        /// If <paramref name="data"/> is null, the object will be constructed using data extracted from default gateway.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="identifier">The identifier of instance to get.</param>
        /// <param name="data">The data used for object construction or null for using data from default gateway.</param>
        /// <returns>The instance with specified identifier.</returns>
        /// <exception cref="PersistingRuntimeException">Error occured during get object by identifier.</exception>
        public TInstance GetInstance<TInstance, TIdentifier>(TIdentifier identifier, OverStore.Runtime.Storage.TypedRecord data)
            where TInstance : class
        {
            var registry = GetRegistry<TInstance, TIdentifier>();
            return registry.GetInstance(identifier, data);
        }

        #endregion

        #region Reader members

        /// <summary>
        /// Return enumerator throught object sequence.
        /// </summary>
        /// <typeparam name="TItem">The type of the item.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter for command.</typeparam>
        /// <param name="parameter">The parameter for command.</param>
        /// <returns>
        /// The sequence of object read from data storage.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during creating object reader.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameter with specified type is not supported.</exception>
        public IEnumerable<TItem> ExecuteObjectReader<TItem, TParameter>(TParameter parameter)
            where TItem : class
            where TParameter : class
        {
            var repository = GetRepository<TItem>();
            return repository.ExecuteObjectReader<TParameter>(parameter);
        }

        /// <summary>
        /// Return enumerator throught object set.
        /// </summary>
        /// <typeparam name="TItem">The type of the item.</typeparam>
        /// <returns>
        /// The sequence of object read from data storage.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during creating object reader.</exception>
        public IEnumerable<TItem> ExecuteObjectReader<TItem>()
            where TItem : class
        {
            var repository = GetRepository<TItem>();
            return repository.ExecuteObjectReader();
        }

        #endregion

        /// <summary>
        ///     <para>
        /// Saves changes made in every instace registered in data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage.</para>
        /// </summary>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        public void SaveAll()
        {
            foreach (var rep in _ObjectRepositoryTypeIndex.Values)
                rep.SaveAll();
        }
    }
}